#include <cassert>
#include "llvm/ADT/ScopedHashTable.h"
#include "mlir-c/IR.h"
#include "mlir/IR/MLIRContext.h"
#include "Ch2/include/toy/Dialect.h"
#include "cpp_side.h"

void mlirLoadToyDialect(MlirContext rawContext) {
  auto context = (mlir::MLIRContext *)rawContext.ptr;
  assert(context != nullptr && "context is null");

  context->getOrLoadDialect<mlir::toy::ToyDialect>();
}

void *mlirScopedHashTableCreate() {
  return new llvm::ScopedHashTable<llvm::StringRef, mlir::Value>();
}

void mlirScopedHashTableDestroy(void *scopedHashTable) {
  assert(scopedHashTable != nullptr && "scopedHashTable is null");

  auto sht =
      (llvm::ScopedHashTable<llvm::StringRef, mlir::Value> *)scopedHashTable;
  assert(sht != nullptr && "convert scopedHashTable error");

  delete sht;
}

const void *mlirScopedHashTableScopeCreate(void *scopedHashTable) {
  assert(scopedHashTable != nullptr && "scopedHashTable is null");

  auto sht =
      (llvm::ScopedHashTable<llvm::StringRef, mlir::Value> *)scopedHashTable;
  assert(sht != nullptr && "convert scopedHashTable error");

  return new llvm::ScopedHashTableScope<llvm::StringRef, mlir::Value>(*sht);
}

void mlirScopedHashTableScopeDestroy(const void *scopedHashTableScope) {
  assert(scopedHashTableScope != nullptr && "scopedHashTableScope is null");

  auto scope =
      (const llvm::ScopedHashTableScope<llvm::StringRef, mlir::Value> *)
          scopedHashTableScope;
  assert(scope != nullptr);

  delete scope;
}

int mlirDeclare(void *scopedHashTable, const char *name, size_t length, MlirValue rawValue) {
  assert(scopedHashTable != nullptr && "scopedHashTable is null");
  assert(name != nullptr && "name is null");
  assert(!mlirValueIsNull(rawValue) && "rawValue is null");

  auto sht =
      (llvm::ScopedHashTable<llvm::StringRef, mlir::Value> *)scopedHashTable;
  assert(sht != nullptr && "convert scopedHashTable error");

  auto key = llvm::StringRef(name, length);

  if (sht->count(key)) {
    return 0;
  }

  auto value = mlir::Value::getFromOpaquePointer(rawValue.ptr);

  sht->insert(key, value);
  return 1;
}

MlirValue mlirLookup(void *scopedHashTable, const char *name, size_t length) {
  assert(scopedHashTable != nullptr && "scopedHashTable is null");
  assert(name != nullptr && "name is null");

  auto sht =
      (llvm::ScopedHashTable<llvm::StringRef, mlir::Value> *)scopedHashTable;
  assert(sht != nullptr && "convert scopedHashTable error");

  auto key = llvm::StringRef(name, length);
  auto value = sht->lookup(key);
  return MlirValue{value.getAsOpaquePointer()};
}
